Amazon Connect 公式ブートキャンプをやってみよう! – STEP 2「発信者番号による顧客情報の照会と本人確認」
みなさん、こんにちは!
福岡オフィスの青柳です。
AWSから公開されている「Amazon Connect Bootcamp」を題材に「ステップ・バイ・ステップ」で構築方法を解説する連載記事の第3回目です。
- STEP 0:「準備編」
- STEP 1:「メニューを備えた基本的な問い合わせフロー」
- STEP 2:「発信者番号による顧客情報の照会と本人確認」 ← 当記事です
- STEP 3:「新規問い合わせ時のコールバックの実装」
- STEP 4:「既存問い合わせ対応の実装」
- STEP 5:「メニューをDTMFからAmazon Lexへ変更」
今回は、Amazon Connectが「DynamoDB」「Lambda」などのAWSサービスと連係するための手法を実践します。
今回の構築範囲
今回は、図で「赤線」で囲まれているフロー、すなわち
- Flow 3:「顧客情報の照会」
- Flow 4:「本人確認」
の処理を作り込んでいきます。
データベースおよびDBアクセスの仕組みを構築
フローの構築に入る前に、まず「顧客情報」を格納するデータベースと、データベースから必要な情報を取得する仕組みを構築します。
実際の運用環境では、顧客情報データベースは大規模なリレーショナルデータベースやCRM (顧客関係管理システム) である場合も多いと思いますが、今回は簡易的なデータベースをAmazon DynamoDBで作成します。
DynamoDBの作成
AWSマネジメントコンソールで「DynamoDB」を開き、「テーブルの作成」を選択します。
今回は以下の項目のみ設定し、他の項目は全てデフォルトのままとします。
- テーブル名: 「ConnectBootCamp」
- パーティションキー: 「phoneNumber」(文字列)
テーブルの作成後、テーブルへアイテム (項目) を追加します。
属性名 | 値 | タイプ |
---|---|---|
phoneNumber (パーティションキー) |
テストで使う自分の電話番号 (例:+813XXXXXXXX) | 文字列 |
fName | あかり | 文字列 |
lName | 彩木 | 文字列 |
DOB | 0707 | 文字列 |
status | シルバー | 文字列 |
caseNumber | 1234 | 文字列 |
caseNotes | ステータス。優先度を上げて対応中。コメント。お問い合わせ内容は現在調査中です、午後3時に状況を更新します。 | 文字列 |
lastAgent | saburo | 文字列 |
電話番号は、国際電話を掛ける時などに使用する「E.164形式」である必要があります。
- 最初に、日本の国番号「+81」を付ける
- 市外局番や携帯電話番号の先頭の「0」は外す
例えば、あなたの電話番号が「03-XXXX-XXXX」であれば、E.164形式の電話番号は「+813XXXXXXXX」となります。
「DOB」は「Date Of Birth」つまり誕生日です。 今回のシステムでは、誕生日の「月・日」を表す4桁の数字を、本人確認の手段として使います。
「status」以降は、顧客の情報や、コールセンターで顧客を対応した履歴情報が格納されています。 実際は、データベースへ顧客情報や対応履歴を書き込んだり更新したりする処理も必要になってきますが、今回は「既に情報が格納されている」状況を前提としています。
IAMロールの作成
DynamoDBから情報を取得するLambda関数を作成します。
その前に、Lambda関数にアクセス権限を付与するIAMロールを作成しましょう。
Lambda関数にアクセス権限を与えるためのIAMロールですので、ユースケースは「Lambda」を選択します。
以下のポリシー (AWS管理ポリシー) を選択します。
- AmazonDynamoDBReadOnlyAccess
- AWSLambdaBasicExecutionRole
タブの設定は省略して、ロール名を以下のように設定します。
- ロール名: 「ConnectBootCamp」
また、設定しようとしている内容が以下の通りになっていることを再確認します。
- 信頼されたエンティティ: 「AWSのサービス: lambda.amazonaws.com」
- ポリシー: 「AmazonDynamoDBReadOnlyAccess」「AWSLambdaBasicExecutionRole」
上記を確認してIAMロールの作成を完了します。
Lambda関数の作成
Lambda関数を作成します。
関数名とランタイムを以下のように設定します。
- 関数名: 「ConnectBootCamp」
- ランタイム: 「Python 3.9」
また、作成したIAMロールをLambda関数に紐付けるために、以下のように設定します。
- 「デフォルトの実行ロールの変更」をクリックして展開
- 実行ロール: 「既存のロールを使用する」を選択
- 既存のロール: 「ConnectBootCamp」を選択
Lambda関数を作成しましたら、コードを入力します。
AWSがAmazon Connect Bootcampで公開しているサンプルコードを、コピー&ペーストでそのまま貼り付けます。
(https://amazon-connect-introduction.workshop.aws/5.lab3.html に掲載されているコードを転載します)
# MIT No Attribution # Copyright 2021 AWS # Permission is hereby granted, free of charge, to any person obtaining a copy of this # software and associated documentation files (the "Software"), to deal in the Software # without restriction, including without limitation the rights to use, copy, modify, # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #Import libraries and set table name import json import boto3 import os tableName= os.environ['tableName'] dynamodb = boto3.resource('dynamodb') def lambda_handler(event, context): #Customer phone number passed from Amazon Connect via JSON phoneNumber = event ['Details']['ContactData']['CustomerEndpoint']['Address'] #customer lookup to DynamoDB via Customer phone number table = dynamodb.Table(tableName) response = table.get_item(Key={'phoneNumber' : phoneNumber}) #If record exists write values to variables if 'Item' in response: caseNumber = response['Item']['caseNumber'] DOB = response['Item']['DOB'] fName = response['Item']['fName'] lName = response['Item']['lName'] status = response['Item']['status'] lastAgent = response['Item']['lastAgent'] caseNotes = response['Item']['caseNotes'] #Return variables to Amazon Connect return {'message': 'Success', 'caseNumber' : caseNumber, 'DOB' : DOB, 'fName' : fName, 'lName' : lName, 'status' : status, 'caseNotes' : caseNotes, 'lastAgent' : lastAgent } #If no match return a default message else: return { 'message': 'Fail'}
※ 転載元ページにも記載されているように、これはサンプルコードですので、このコードを本番運用環境で使う際には内容をよく精査した上で使用してください。
コードを張り付けた後に「Deploy」をクリックして反映させます。
次に、「設定」タブを開き、左側の「環境変数」を選択します。
環境変数を設定するために「編集」をクリックします。
以下の環境変数を設定します。
- キー: 「tableName」
- 値: 「ConnectBootCamp」
これでLambda関数の準備ができました。
Lambda関数をテストする
作成したLambda関数が正常に動作するかどうか、テストしておきます。
「コード」タブに戻り、「Test」をクリックします。
任意のイベント名を入力して、イベントデータのJSONを以下のように入力します。
{ "Details": { "ContactData": { "CustomerEndpoint": { "Address": "+813XXXXXXXX", "Type": "TELEPHONE_NUMBER" } } } }
※ 電話番号は実際に使用する番号を入力してください。
テストが実行され、正常に動作すれば以下のレスポンスが返ってくるはずです。
{ "message": "Success", "caseNumber": "1234", "DOB": "0707", "fName": "あかり", "lName": "彩木", "status": "シルバー", "caseNotes": "ステータス。優先度を上げて対応中。コメント。お問い合わせ内容は現在調査中です、午後3時に状況を更新します。", "lastAgent": "saburo" }
「message」の「Success」は、キーとなる電話番号から顧客情報を正常に取得できたことを示します。
なお、データベースに登録されていない電話番号をキーに情報取得を試みると、以下のレスポンスが返ってきます。 (余裕があれば試してみてください)
{ "message": "Fail" }
これらのようにレスポンスのJSONが返ってくれば動作テストは成功ですが、もし、想定外の結果が返ってきたりエラーが発生した場合には、ここまでの設定を再確認してください。
- DynamoDBのテーブル名やキー名は正しいか
- IAMロールの設定は正しいか
- Lambda関数の設定は正しいか
- 実行ロールは適切に設定されているか
- 環境変数は正しく設定されているか
Amazon ConnectがLambda関数を呼び出せるように設定する
作成したLambda関数は、そのままではAmazon Connectから利用することはできません。
予めAmazon Connectインスタンスで利用するLambda関数を登録しておく必要があります。
AWSマネジメントコンソールでAmazon Connectインスタンスを選択して、画面左側のメニューから「問い合わせフロー」を選択します。
下の方にスクロールすると「AWS Lambda」の設定項目があります。
Lambda関数「ConnectBootCamp」を選択して、「+ Add Lambda Function」をクリックします。
Lambda関数が登録されたことを確認します。
これで、Amazon Connectインスタンスの問い合わせフローからLambda関数を呼び出すための準備ができました。
フローの構築
では、ここからはフローの構築に入っていきます。
Flow 3「顧客情報の照会」
フローの全体図はこのようになります。
図の(1)、(4)、(5)に相当するブロックは、前回のステップにて作成済みです。
今回は(2)と(3)のブロックを追加します。
(2) 「統合」-「AWS Lambda関数を呼び出す」
フローからLambda関数を呼び出すことができるブロックです。
- 関数のARN: 「関数を選択する」を選択
- 関数名: 「ConnectBootCamp」を選択
- タイムアウト: 「8秒」と入力
登録済みのLambda関数から選択できるようになっているはずです。 もし「ConnectBootCamp」が選択肢に現れない場合は、ここまでの設定を見直してください。
Lambda関数を呼び出すと、フローの現在の状況がイベントデータ経由でLambda関数へ渡され、処理が行われた後、レスポンスが返ってきます。
電話を掛けてきた顧客の電話番号がLambda関数へ渡すイベントデータに含まれているため、Lambda関数は電話番号をキーにDynamoDBから情報を取得できるという訳です。
(3) 「設定」-「コンタクト属性の設定」
Lambda関数から返ってきたレスポンスは、そのままではフロー内で利用することができません。
この後のフローでレスポンス内容を利用できるようにするため、「ユーザー定義」属性に値をコピーします。
設定項目がかなり多いため、いくつかに分けます。
「コンタクト属性」(あるいは単に「属性」) の種類の一つである「ユーザー定義」属性は、自由に値を代入することができます。
一方、Lambda関数から返ってきたレスポンスは「外部」属性に格納されており、Lambda関数を呼び出した直後のみ値を参照することができます。
ここでは、Lambda関数のレスポンスに含まれる項目名と同名の「ユーザー定義」属性を用意して、「外部」属性から読み出した値を代入します。
- 保存する属性 (=代入先)
- 宛先タイプ: 「ユーザー定義」を選択
- 宛先属性: 「message」と入力
- 設定する値 (=代入元)
- 「属性を使用する」を選択する
- タイプ: 「外部」を選択
- 属性: 「message」と入力
同様にして、Lambda関数からのレスポンスに含まれる8個の項目を「ユーザー定義」属性に代入していきます。
- 保存する属性 (=代入先)
- 宛先タイプ: 「ユーザー定義」を選択
- 宛先属性: 「caseNumber」と入力
- 設定する値 (=代入元)
- 「属性を使用する」を選択する
- タイプ: 「外部」を選択
- 属性: 「caseNumber」と入力
- 保存する属性 (=代入先)
- 宛先タイプ: 「ユーザー定義」を選択
- 宛先属性: 「DOB」と入力
- 設定する値 (=代入元)
- 「属性を使用する」を選択する
- タイプ: 「外部」を選択
- 属性: 「DOB」と入力
- 保存する属性 (=代入先)
- 宛先タイプ: 「ユーザー定義」を選択
- 宛先属性: 「fName」と入力
- 設定する値 (=代入元)
- 「属性を使用する」を選択する
- タイプ: 「外部」を選択
- 属性: 「fName」と入力
- 保存する属性 (=代入先)
- 宛先タイプ: 「ユーザー定義」を選択
- 宛先属性: 「lName」と入力
- 設定する値 (=代入元)
- 「属性を使用する」を選択する
- タイプ: 「外部」を選択
- 属性: 「lName」と入力
- 保存する属性 (=代入先)
- 宛先タイプ: 「ユーザー定義」を選択
- 宛先属性: 「status」と入力
- 設定する値 (=代入元)
- 「属性を使用する」を選択する
- タイプ: 「外部」を選択
- 属性: 「status」と入力
- 保存する属性 (=代入先)
- 宛先タイプ: 「ユーザー定義」を選択
- 宛先属性: 「caseNotes」と入力
- 設定する値 (=代入元)
- 「属性を使用する」を選択する
- タイプ: 「外部」を選択
- 属性: 「caseNotes」と入力
- 保存する属性 (=代入先)
- 宛先タイプ: 「ユーザー定義」を選択
- 宛先属性: 「lastAgent」と入力
- 設定する値 (=代入元)
- 「属性を使用する」を選択する
- タイプ: 「外部」を選択
- 属性: 「lastAgent」と入力
合計で8個の項目を設定しましたら、このブロックの設定は終わりです。
最後に、ブロック間の線を繋ぎ直して「Flow 3」の更新は完了です。
Flow 4「本人確認」
フローの全体図はこのようになります。
図の(1)、(8)、(9)に相当するブロックは、前回のステップにて作成済みです。
今回は(2)~(7)のブロックを追加します。
(2) 「ブランチ」-「コンタクト属性を確認する」
「ユーザー定義」の属性「message」の値を確認します。
- 確認する属性:
- タイプ: 「ユーザー定義」を選択する
- 属性: 「message」を選択する
- チェックする属性:
- 「別の条件の追加」をクリックする
- 「等しい」「Success」を選択する
この「message」属性には、Lambda関数のレスポンスに含まれる「message」の値が代入されています。 Lambda関数の作成手順で説明しましたが、「message」の値は、顧客情報が正常に取得できた場合は「Success」、正常に取得できなかった場合は「Fail」となります。
つまり、このブロックでは「顧客の電話番号をキーに顧客情報が正常に取得できたか否か」を判定しています。
顧客情報を取得できた場合は(3)へ進み、「本人確認」の処理を行います。
顧客情報を取得できなかった場合は(8)へ進み、「本人確認」の処理はスキップして、次のフロー「Flow 5:メニューの選択」へ遷移します。
(3) 「操作」-「プロンプトの再生」
「本人確認」の処理に進む前に、まずは「お得意様」(?) に対して挨拶のメッセージを流します。 更に、固定の挨拶メッセージに続いて、取得した顧客情報を使って「顧客に応じてカスタマイズされたアナウンス」も読み上げることにしましょう。
- プロンプト: 「テキスト読み上げまたはチャットテキスト」→「テキストの入力」の順に選択
- テキスト: 「いつもご利用頂きましてありがとうございます。 $.Attributes.lName 様は $.Attributes.status メンバーでございます。」と入力
- 解釈する: 「テキスト」を選択
プロンプトのテキストの中で「$」記号を使うと、コンタクト属性に設定されている値をプロンプトに埋め込むことができます。 「$.Attributes」で参照したい属性が「ユーザー定義」属性であることを指定し、最後のピリオドに続いて「属性名」を指定します。
「lName」の値は「彩木」、「status」の値は「シルバー」がそれぞれ格納されていますので、ここで実際に読み上げられるテキストは「彩木様はシルバーメンバーでございます」となります。 (「彩木」は本当は「さいき」なんですが「あやき」と読み上げられるのはご愛敬ですね)
(4) 「操作」-「顧客の入力を保存する」
ここからは「本人確認」の処理になります。
項目が多いため、前半と後半に分けます。
まず、顧客に入力を促すためのアナウンスを設定します。
- プロンプト: 「テキスト読み上げまたはチャットテキスト」→「テキストの入力」の順に選択
- テキスト: 「お客様の誕生日を4桁の数字で入力してください。数字は、月を2桁、日を2桁、の順で入力してください。」と入力
- 解釈する: 「テキスト」を選択する
前回のステップでは、顧客にメニューを選択してもらうために「1」「2」「3」のキー入力を受け付けるようにしました。
ここでは、本人確認のために誕生日を表す「4桁の数字」を入力してもらう必要があるため、連続したキー入力を受け付けるようにします。
- 顧客の入力: 「カスタム」を選択
- 最大桁数: 「4」を入力
- 最初のエントリ前のタイムアウト: 「10秒」と入力 (適宜調整してください)
その他の項目はデフォルトのままにします。
メニュー選択で使用した「顧客の入力を取得する」ブロックとは異なり、この「顧客の入力を保存する」ブロックでは入力値に応じて処理分岐は行われません。 (正しく入力された場合は、常に出力先は「成功」となります)
(5) 「ブランチ」-「コンタクト属性を確認する」
「顧客の入力を保存する」ブロックで入力された「4桁の数字」が、顧客情報DBに格納された「誕生日」の情報と合致するか、比較を行います。
- 確認する属性:
- タイプ: 「システム」を選択する
- 属性: 「保存済みの顧客の入力」を選択する
- チェックする属性:
- 「別の条件の追加」をクリックする
- 「等しい」「$.Attributes.DOB」を選択する
前のブロック「顧客の入力を保存する」で顧客が入力された値は、タイプ「システム」に属する「保存済みの顧客の入力」という属性に格納されています。
比較先は「ユーザー定義」の属性「DOB」の値となりますが、ここで、さきほどプロンプトのテキストにコンタクト属性の値を埋め込むために使った「$」記号による記述を使います。
(6) 「操作」-「プロンプトの再生」
顧客が入力した「4桁の数字」と、顧客情報の「誕生日」が一致した場合、すなわり「本人確認」が成功した場合です。
- プロンプト: 「テキスト読み上げまたはチャットテキスト」→「テキストの入力」の順に選択
- テキスト: 「お客様の登録を確認できました。」と入力
- 解釈する: 「テキスト」を選択
(7) 「操作」-「プロンプトの再生」
「4桁の数字」と「誕生日」が一致しなかった場合は、「本人確認」に失敗したとみなします。
- プロンプト: 「テキスト読み上げまたはチャットテキスト」→「テキストの入力」の順に選択
- テキスト: 「登録されている誕生日と一致しませんでした。」と入力
- 解釈する: 「テキスト」を選択
「本人確認」に成功した場合、失敗した場合、それぞれ(8)へ進み、次のフロー「Flow 5:メニューの選択」へ遷移します。
実は「本人確認」を行った結果によって処理が変わるのは、(6)(7)で流すアナウンスの違いのみであり、その後すぐにルートが合流してしまうので、アナウンスの他には何も違いはありません。 これでは「本人確認」の意味が無いですが、実際の運用環境では「認証済みユーザー」のフラグを立てる等を行い、本人確認が行われたか否かを適切に処理できるようにフローを組み立てる必要がありますね。
動作テスト
さて、これで「STEP 2:発信者番号による顧客情報の照会と本人確認」の全ての設定が終わりました。
例によって、実際に電話を掛けてみて動作を確認しましょう。
なお、今回はエージェントに電話が繋がるところまで行かなくても動作は確認できるので、エージェントのログイン・CCP起動は必須ではありません。
テストシナリオ 1:登録された電話番号から電話を掛けて、本人確認を行う
DynamoDBデータベースの「phoneNumber」に登録した電話番号から、電話を掛けてみます。
本人確認のために電話番号の入力を求められますので、「DOB」に登録した誕生日を入力します。
- 電話を掛ける
- 呼び出し音の後「お電話ありがとうございます」のアナウンスが流れる
- 「いつもご利用頂きましてありがとうございます。彩木様はシルバーメンバーでございます」のアナウンスが流れる
- 「お客様の誕生日を4桁の数字で入力してください」のアナウンスが流れる
- プッシュボタンで「0」「7」「0」「7」と入力する
- 「お客さまの登録を確認できました」のアナウンスが流れる
- 「購入前のご相談は1を、・・・」のアナウンスが流れる
このような挙動をすれば動作OKです。
もし「いつもご利用頂きまして・・・」のアナウンスが流れない場合は、電話の「発信者番号通知」が有効になっていることを確認してみてください。 (発信者番号通知が無効になっている電話からは、頭に「186」を付けて掛けると発信者番号が通知されます)
テストシナリオ 2:本人確認に失敗する
今度は、登録済みの電話番号から電話を掛けた後に、敢えて本人確認に失敗してみます。
- 電話を掛ける
- 呼び出し音の後「お電話ありがとうございます」のアナウンスが流れる
- 「いつもご利用頂きましてありがとうございます。彩木様はシルバーメンバーでございます」のアナウンスが流れる
- 「お客様の誕生日を4桁の数字で入力してください」のアナウンスが流れる
- プッシュボタンで「1」「2」「3」「4」など適当な4桁の数字を入力する (もしくは「何も押さずにタイムアウトを待つ」でも構いません)
- 「登録されている誕生日と一致しませんでした」のアナウンスが流れる
- 「購入前のご相談は1を、・・・」のアナウンスが流れる
このような挙動をすれば動作OKです。
テストシナリオ 3:登録されていない電話番号から電話を掛ける
最後に、登録されていない電話番号から電話を掛けてみます。
いくつか方法がありますが、どれでも問題ありません。
- 違う電話から掛ける
- DynamoDBデータベースの「phoneNumber」の値を一時的に変える
- 発信者番号非通知で掛ける (頭に「184」を付けて掛ける)
以下のような挙動をすればOKです。
- 電話を掛ける
- 呼び出し音の後「お電話ありがとうございます」のアナウンスが流れる
- 「購入前のご相談は1を、・・・」のアナウンスが流れる
これで、テストパターンを一通り確認することができました。 もし上手く行かない場合は、まずはここまでの設定内容を確認してみてください。
また、トラブルシューティングのために各種ログを確認することも有効です。
- フローログを確認する (CloudWatch Logs)
- ブロックごとにログが記録されているので、意図した動作をしているか一つずつ確認
- 発信者電話番号がログに記録されているので、番号が正しく認識されているか確認 (違う番号だったり非通知だったりしないか)
- Lambda関数から受け取った顧客情報のデータが「ユーザー定義」属性に正しく格納されたかどうか確認
- Lambda関数の実行ログを確認する (CloudWatch Logs)
- 「Success」が返っているかどうか確認
- レスポンスに顧客情報のデータが正しく入っているかどうか確認
おわりに
今回は「発信者番号による顧客情報の照会と本人確認」の構築を行いました。
DynamoDBによる簡易的な「顧客情報」データベースではありますが、顧客情報を使ったアナウンスのカスタマイズや、本人確認が実装できることが確認できたのではないかと思います。
次のステップは「STEP 3:新規問い合わせ時のコールバックの実装」です。 是非ご覧ください。